//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved

#include "pch.h"
#include <mfidl.h>
#include <mfapi.h>
#include <mfreadwrite.h>
#include "MediaReader.h"
#include "DirectXHelper.h"

using namespace Microsoft::WRL;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;
using namespace Windows::Foundation;
using namespace Windows::ApplicationModel;

MediaReader::MediaReader()
{
   ZeroMemory(&m_waveFormat, sizeof(m_waveFormat));
   m_installedLocation     = Package::Current->InstalledLocation;
   m_installedLocationPath = Platform::String::Concat(m_installedLocation->Path, "\\");
}


WAVEFORMATEX *MediaReader::GetOutputWaveFormatEx()
{
   return(&m_waveFormat);
}


Platform::Array<byte> ^ MediaReader::LoadMedia(_In_ Platform::String ^ filename)
{
   DX::ThrowIfFailed(
      MFStartup(MF_VERSION)
      );

   ComPtr<IMFSourceReader> reader;
   DX::ThrowIfFailed(
      MFCreateSourceReaderFromURL(
         Platform::String::Concat(m_installedLocationPath, filename)->Data(),
         nullptr,
         &reader
         )
      );

   // Set the decoded output format as PCM.
   // XAudio2 on Windows can process PCM and ADPCM-encoded buffers.
   // When using MediaFoundation, this sample always decodes into PCM.
   Microsoft::WRL::ComPtr<IMFMediaType> mediaType;
   DX::ThrowIfFailed(
      MFCreateMediaType(&mediaType)
      );

   DX::ThrowIfFailed(
      mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio)
      );

   DX::ThrowIfFailed(
      mediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM)
      );

   DX::ThrowIfFailed(
      reader->SetCurrentMediaType(static_cast<uint32>(MF_SOURCE_READER_FIRST_AUDIO_STREAM), 0, mediaType.Get())
      );

   // Get the complete WAVEFORMAT from the Media Type.
   Microsoft::WRL::ComPtr<IMFMediaType> outputMediaType;
   DX::ThrowIfFailed(
      reader->GetCurrentMediaType(static_cast<uint32>(MF_SOURCE_READER_FIRST_AUDIO_STREAM), &outputMediaType)
      );

   UINT32       size = 0;
   WAVEFORMATEX *waveFormat;
   DX::ThrowIfFailed(
      MFCreateWaveFormatExFromMFMediaType(outputMediaType.Get(), &waveFormat, &size)
      );

   CopyMemory(&m_waveFormat, waveFormat, sizeof(m_waveFormat));
   CoTaskMemFree(waveFormat);

   PROPVARIANT propVariant;
   DX::ThrowIfFailed(
      reader->GetPresentationAttribute(static_cast<uint32>(MF_SOURCE_READER_MEDIASOURCE), MF_PD_DURATION, &propVariant)
      );
   // 'duration' is in 100ns units; convert to seconds, and round up
   // to the nearest whole byte.
   LONGLONG     duration = propVariant.uhVal.QuadPart;
   unsigned int maxStreamLengthInBytes =
      static_cast<unsigned int>(
         ((duration * static_cast<ULONGLONG>(m_waveFormat.nAvgBytesPerSec)) + 10000000) /
         10000000
         );

   Platform::Array<byte> ^ fileData = ref new Platform::Array<byte>(maxStreamLengthInBytes);

   ComPtr<IMFSample>      sample;
   ComPtr<IMFMediaBuffer> mediaBuffer;
   DWORD flags = 0;

   int  positionInData = 0;
   bool done           = false;
   while (!done)
   {
      DX::ThrowIfFailed(
         reader->ReadSample(static_cast<uint32>(MF_SOURCE_READER_FIRST_AUDIO_STREAM), 0, nullptr, &flags, nullptr, &sample)
         );

      if (sample != nullptr)
      {
         DX::ThrowIfFailed(
            sample->ConvertToContiguousBuffer(&mediaBuffer)
            );

         BYTE  *audioData         = nullptr;
         DWORD sampleBufferLength = 0;
         DX::ThrowIfFailed(
            mediaBuffer->Lock(&audioData, nullptr, &sampleBufferLength)
            );

         for (DWORD i = 0; i < sampleBufferLength; i++)
         {
            fileData[positionInData++] = audioData[i];
         }
      }
      if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
      {
         done = true;
      }
   }

   return(fileData);
}
